package com.hbcx;

import com.alibaba.fastjson2.JSONObject;
import com.hbcx.dto.*;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.*;

import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

/**
 * 浏览器运行工具
 *
 */
public class App 
{
    private static Map<String, String> config;
    public static void main( String[] args ) throws IOException, InterruptedException, NoSuchFieldException, IllegalAccessException {
        //获得配置信息
        config = getConfig("config.conf");
        String actionFile = getActionFile(args);
        WebDto webDto = getActions(actionFile);
        int loop = webDto.getLoop();

        //获得浏览器驱动
        WebDriver driver = getDriver();
        while (loop-->0)
        {
            runInstance(webDto,driver);
            for (SimuAction action : webDto.getActions()) {
                for (ElementAction elementAction : action.getElementActions()) {
                    elementAction.setElement(null);
                }
            }
        }
    }
    public static String getActionFile(String[] actionFile) {
        //优先级如下：
        //1.命令行中是否有参数。
        //2.config.conf中是否配置了默认文件
        //3.simuaction.json文件
        if(actionFile.length==0){
            return config.get("defaultActionFile")==null?"simuaction.json":config.get("defaultActionFile");
        }else
        {
            return actionFile[0];
        }
    }
    public static Map<String,String> widowsMap = new LinkedHashMap<>();

    public static void runInstance(WebDto webDto,WebDriver driver) throws IOException, InterruptedException, NoSuchFieldException, IllegalAccessException {
        driver.get(webDto.getUrl());
        if(config.get("windowSize")!=null){
            String[] size = config.get("windowSize").split(",");
            if(size.length==2)
            {
                driver.manage().window().setSize(new Dimension(Integer.parseInt(size[0]), Integer.parseInt(size[1])));
            }
        }

        widowsMap.put("root",driver.getWindowHandle());
        int timeOut = config.get("timeOut")==null?30:Integer.parseInt(config.get("timeOut"));
        WebDriverWait driverWait = new WebDriverWait(driver, Duration.ofSeconds(timeOut));
        for (SimuAction action : webDto.getActions()) {
            List<ElementAction> groupActionList = new ArrayList<>();
            Group curGroup=null;
            for (ElementAction elementAction : action.getElementActions()) {
                if(elementAction.getBy()!=null && (elementAction.getGroup()==null && curGroup==null))
                {
                    waitAndDoAction(driver,driverWait,elementAction,timeOut);
                }else if(elementAction.getGroup() !=null || curGroup!=null){
                    if(elementAction.getGroup()!=null && elementAction.getGroup().getType().equalsIgnoreCase("start"))//是起始位置
                    {
                        curGroup = elementAction.getGroup();
                        groupActionList.add(elementAction);

                    }else if(elementAction.getGroup()!=null
                            && elementAction.getGroup().getType().equalsIgnoreCase("end"))
                    {
                        //结束时，执行这些动作.
                        if(curGroup==null || !curGroup.getName().equalsIgnoreCase(elementAction.getGroup().getName()))
                        {
                            throw new RuntimeException("结束组的信息与当前组不一致，请检查");
                        }
                        groupActionList.add(elementAction);
                        System.out.println("开始执行动作组，组的长度为"+groupActionList.size());
                        for (Object curValue : curGroup.getGroupVarValue()) {
                            for (int i = 0; i < groupActionList.size(); i++) {
                                ElementAction curElementAction = groupActionList.get(i).cloneAction();
                                if(i==0)
                                {
                                    String fieldName = curGroup.getGroupVarField();
                                    Class<ElementAction> clz = ElementAction.class;
                                    Field field = clz.getDeclaredField(fieldName);
                                    field.setAccessible(true);
                                    field.set(curElementAction,curValue);
                                }
                                waitAndDoAction(driver,driverWait,curElementAction,timeOut);
                            }
                        }
                        //清理组信息
                        curGroup = null;
                        groupActionList.clear();
                    }else if(elementAction.getGroup()==null && curGroup!=null)
                    {
                        groupActionList.add(elementAction);
                    }
                }

            }
        }
//        int i= System.in.read();
        System.out.println("完成所有任务");
    }
    public static void waitAndDoAction(WebDriver driver,WebDriverWait driverWait,ElementAction elementAction,int timeOut) throws InterruptedException, IOException {
        boolean flag = true;
        while (flag)
        {
            try{
                WebDriver targetDriver;
                if(elementAction.getFrame()!=null)
                {
                    By frameBy = elementAction.getBy(elementAction.getFrameByType(), elementAction.getFrame());
                    driverWait.until(ExpectedConditions.presenceOfElementLocated(frameBy));
                    WebElement wb= driver.findElement(By.xpath(elementAction.getFrame()));
                    targetDriver= driver.switchTo().frame(wb);

                    WebDriverWait driverWait2 = new WebDriverWait(targetDriver, Duration.ofSeconds(timeOut));
                    if(waitAction(driverWait2,elementAction)){
                        doAction(elementAction,targetDriver);
                    }
                    driver.switchTo().defaultContent();
                }else
                {
                    targetDriver = driver;
                    if(waitAction(driverWait,elementAction))
                    {
                        doAction(elementAction,targetDriver);
                    }
                }
                flag = false;
            }catch (TimeoutException | InterruptedException e) {
                System.out.println("等待元素超时，要继续等待么？ y(继续)/n(停止)");
                int b = System.in.read();
                if (b == 'n' || b == 'N') {
                    throw e;
                }
            }
        }
        if(elementAction.getAfterPause()!=0)
        {
            Thread.sleep(elementAction.getAfterPause()* 1000L);
        }
    }
    public static boolean waitAction(WebDriverWait wait,ElementAction elementAction){
        if(elementAction.getActionType()!=null && elementAction.getActionType().equalsIgnoreCase("waitNo")) {
            System.out.printf("等待元素消失: " + elementAction.getName() + " \n");

            wait.until(ExpectedConditions.numberOfElementsToBe(elementAction.getBy(),0));
            return false;
        }else {
            System.out.printf("等待元素到位: " + elementAction.getName() + " \n");

            wait.until(ExpectedConditions.presenceOfElementLocated(elementAction.getBy()));
            return true;
        }
    }

    public static void doAction(ElementAction elementAction,WebDriver webDriver) throws InterruptedException {
        elementAction.fillElement(webDriver);

        if(elementAction.getWindowsAction()!=null)
        {
            WindowsAction action = elementAction.getWindowsAction();;
        }

        elementAction.doAction(webDriver);

        if(elementAction.getWindowsAction()!=null)
        {
            WindowsAction action = elementAction.getWindowsAction();;
            if(action.getType().equalsIgnoreCase("autoInter")){
                while (webDriver.getWindowHandles().size()==1)
                {
                    Thread.sleep(500);
                }
                webDriver.getWindowHandles().stream().filter(t->!t.equals(widowsMap.get("root"))).findFirst()
                .ifPresent(t->
                {
                    widowsMap.put(action.getWindowsName(),t);
                    webDriver.switchTo().window(t);
                });
            }else if(action.getType().equalsIgnoreCase("exit")){
//                String curId = widowsMap.get(action.getWindowsName());
                widowsMap.remove(action.getWindowsName());
                webDriver.close();
                webDriver.switchTo().window(widowsMap.get("root"));
            }
        }
    }
    public static WebDto getActions(String configFileName) throws IOException {
        InputStream is = Files.newInputStream(Paths.get(configFileName));
        InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);

        try (BufferedReader reader = new BufferedReader(isr)) {
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            JSONObject jsonObject = JSONObject.parseObject(sb.toString());
            WebDto tc = jsonObject.toJavaObject(WebDto.class);
            return tc;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public static  WebDriver getDriver() throws IOException {

        ChromeDriverService service =
                new ChromeDriverService.Builder().usingDriverExecutable(new File(config.get("drivePath"))).build();
//        DesiredCapabilities capabilities =new  DesiredCapabilities();
//
//        capabilities.setCapability("chrome.binary","res/chromedriver.exe");
//        capabilities.setCapability(ChromeOptions.CAPABILITY,options);

        ChromeOptions options = new ChromeOptions();

        String optionsStr = config.get("options");
        for (String option : optionsStr.split(",")) {
            options.addArguments(option);
        }
        String experimentalStr = config.get("experimentalOption");
        if(experimentalStr!=null && experimentalStr.split(",").length>0)
        {
            HashMap<String, Object> chromePrefs = new HashMap<String, Object>();
            for (String option : experimentalStr.split(",")) {
               chromePrefs.put( option.split("=")[0],  option.split("=")[1]);  // 将下载路径设置为自定义路径
            }
            options.setExperimentalOption("prefs", chromePrefs);
        }

        WebDriver driver = new ChromeDriver(service,options);
        return driver;
    }
    public static Map<String,String> getConfig(String fileName) throws IOException {
        Map<String,String> data = new HashMap<>();
        InputStream is = Files.newInputStream(Paths.get(fileName));
        InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);

        try (BufferedReader reader = new BufferedReader(isr)) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 打印每一行
                String[] blocks = line.split("=",2);
                if(!line.trim().startsWith("#"))
                {
                    data.put(blocks[0].trim(), blocks[1].trim());
                }
            }
        }catch (IOException e) {
            throw e;
        }
        return data;
    }
}
